package edu.calpoly;
import java.util.ArrayList;
import java.util.StringTokenizer;

public abstract class GTP {	
	/*
	 * 6.3.1 Administrative Commands
	 */

	/**
	 * @returns  Version of the GTP Protocol. For this specification 2.
	 */
	String protocol_version(){
		return "2";
	}
	
	/**
	 *  @returns Name of the engine. "GoBot"
	 */
	abstract String name();
	
	/**
	 * @returns Version of the engine. "0.1"
	 */
	abstract String version();
	
	/**
	 * The protocol makes no distinction between unknown commands and 
	 * known but unimplemented ones. Do not declare a command as known if it is 
	 * known not to work.
	 * 
	 * @param command Name of a command
	 * @returns "true" if the command is known by the engine, "false" otherwise
	 */
	abstract String known_command(String command);


	/**
	 *  Include all known commands, including required ones and private 
	 *  extensions.
	 *  @returns List of commands
	 */
	abstract ArrayList<String> list_commands();
	
	/** 
	 * The session is terminated and the connection is closed.<br>
	 * The full response of this command must be sent before the engine closes 
	 * the connection. The controller must receive the response before the 
	 * connection is closed on its side.
	 */
	abstract void quit();
	
	/*
	 * 6.3.2 Setup Commands
	 */
	
	/**
	 * The board size is changed. The board configuration, number of captured 
	 * stones, and move history become arbitrary. In GTP version 1 this command 
	 * also did the work of clear board. This may or may not be true for 
	 * implementations of GTP version 2. Thus the controller must call clear 
	 * board explicitly. Even if the new board size is the same as the old one, 
	 * the board configuration becomes arbitrary.
	 * 
	 * @param size - New size of the board.
	 * @fails Syntax error. If the engine cannot handle the new size, fails 
	 * 			with the error message "unacceptable size".
	 */
	abstract void boardsize(int size);
	
	/** 
	 * The board is cleared, the number of captured stones is reset to zero 
	 * for both colors and the move history is reset to empty.
	 */
	abstract void clear_board();
	
	/**
	 * 	Changes the komi value.
	 * <br>The engine must accept the komi even if it should be ridiculous.
	 * 
	 * @param value new value of komi.
	 */
	abstract void komi(float value);
	
	/**
	 * Handicap stones are placed on the board according to the specication 
	 * in section 4.1.1. 
	 * <br>This command is only valid if the board is empty. See section 4.1.1 
	 * for valid number of handicap stones. The	handicap stones are not 
	 * included in the move history.
	 * 
	 * @param value Number of handicap stones.
	 * @return A list of the vertices where handicap stones have been placed.
	 * @fails Syntax error. invalid number of stones, board not empty
	 */
	abstract ArrayList<String> fixed_handicap(int value);
	
	/**
	 * Handicap stones are placed on the board on the vertices the engine 
	 * prefers. See also section 4.1.2.
	 * <br>This command is only valid if the board is empty. The engine may 
	 * place fewer than the requested number of stones on the board under 
	 * certain circumstances, as discussed in section 4.1.2. The controller can 
	 * check this by counting the number of vertices in the response. The 
	 * handicap	stones are not included in the move history. Vertices must not 
	 * be repeated or include \pass". 
	 * 
	 * @param value Number of handicap stones.
	 * @fails Syntax error, invalid number of stones, board not empty,bad 
	 * 			vertex list
	*/
	abstract ArrayList<String> place_free_handicap(int value);
	
	/**
	 * Handicap stones are placed on the vertices as requested.
	 * This command is only valid if the board is empty. The list must have at 
	 * least two elements and no more than the number of board vertices minus 
	 * one. The engine must accept the handicap placement. The handicap stones 
	 * are not included in the move history. Vertices must not be repeated or 
	 * include \pass".
	 *  
	 * @param vertices A list of vertices where handicap stones should be 
	 * 			placed on the board.
	 * @fails syntax error, board not empty, bad vertex list
	 */
	abstract void set_free_handicap(ArrayList<String> vertices);
	
	/*
	 * 6.3.3 Core Play Commands
	 */
	
	/**
	 *  A stone of the requested color is played at the requested vertex. 
	 *  The number of captured stones is updated if needed and the move is 
	 *  added to the move history.
	 *  <br>Consecutive moves of the same color are not considered illegal from 
	 *  the protocol point of view.
	 * 	
	 * @param color Color of the move
	 * @param vertex Vertex of the move
	 * @fails syntax error, illegal move. In the latter case, fails with the 
	 * 			error message \illegal move".
	 */
	abstract void play(String color, String vertex);
	
	/**
	 * A stone of the requested color is played where the engine chooses. The 
	 * number of captured stones is updated if needed and the move is added to 
	 * the move history.
	 * <br>Notice that \pass" is a valid vertex and should be returned if the 
	 * engine wants to pass. Use \resign" if you want to give up the game. The 
	 * controller is allowed to use this command for either color, regardless 
	 * who played the last move.
	 * 
	 * @param color Color for which to generate a move.
	 * @return Vertex where the move was played or the string \resign".
	 */
	abstract String genmove(String color);
	
	/**
	 * The board conguration and the number of captured stones are reset to the 
	 * state before the last move. The last move is removed from the move 
	 * history.
	 * <br>If you want to take back multiple moves, use this command multiple 
	 * times. The engine may fail to undo if the move history is empty or if 
	 * the engine only maintains a partial move history, which has been 
	 * exhausted by previous undos. It is never possible to undo handicap 
	 * placements. Use clear board if you want to start over. An engine which 
	 * never is able to undo should not include this command among its known 
	 * commands.
	 * 
	 * 	@fails If the engine is unable to take back the last move, fails with 
	 * 			the error message "cannot undo".
	 */
	abstract void undo();
	
      // for turning random on or off
      abstract String random(String cmd);

      // for setting the depth
      abstract String setDepth(String color, int depth);

	/*
	 * 6.3.4 Tournament Commands
	 */
	
	/**
	 * If the engine never is able to determine the score, leave the command 
	 * unimplemented.
	 * 
	 * @return Score as described in section 4.3.
	 * @fails If the engine is unable to determine the score, fails with error 
	 * 			message \cannot score".
	 */
	abstract String final_score();
	
	/**
	 * See section 4.3 for details.
	 * @param status Requested status.
	 * @returns Stones with the requested status.
	 * @fails syntax error
	*/
	abstract ArrayList<String> final_status_list(String status);

	/*
	 * 6.3.6 Debug Commands
	 */
	
	/** 
	 * The engine may draw the board as it likes. It is, however, required to 
	 * place the coordinates as described in section 2.11. This command is only 
	 * intended to help humans with	debugging and the output should never need 
	 * to be parsed	by another program.
	 * @return A diagram of the board position.
	 */
	abstract String showboard();
	
   /**
    * This is the main loop parser for the GTP.  This will parse the inputs 
    * and call the appropriate method to complete that command.
    * @param command The command to be parsed.
    */
   public void parse(String command){
      StringTokenizer parser = new StringTokenizer(command);
      String cmnd = parser.nextToken();
      
      if(cmnd.compareTo("protocol_version") == 0){
         System.out.println("= " + protocol_version());
      }
      
      else if(cmnd.compareTo("name") == 0){
         System.out.println("= " + name());
      }
      
      else if(cmnd.compareTo("version") == 0){
         System.out.println("= " + version());
      }
      
      else if(cmnd.compareTo("known_command") == 0){
         System.out.println("= " + known_command(parser.nextToken()));
      }
      
      else if(cmnd.compareTo("list_commands") == 0){
         System.out.print("= ");
         for(String value: list_commands()){
            System.out.println(value);
         }
      }
      
      else if(cmnd.compareTo("quit") == 0){
    	  //Confirm we got the quit command then go ahead and close up shop.
    	  System.out.println("=");
         quit();
      }
      
      else if(cmnd.compareTo("boardsize") == 0){
         try{
            boardsize(Integer.parseInt(parser.nextToken()));
            System.out.println("= ");
         }catch(Exception e){
            System.out.println("? boardsize not an integer");
         }
      }
      
      else if(cmnd.compareTo("clear_board") == 0){
         clear_board();
	   	  //We're Good.
	   	  System.out.println("=");
      }
      
      else if(cmnd.compareTo("komi") == 0){
         try{
            komi(Float.parseFloat(parser.nextToken()));
            System.out.println("= ");
         }catch(Exception e){
            System.out.println("? komi not a float");
         }
      }
      
      else if(cmnd.compareTo("fixed_handicap") == 0){
    	  try{
	         fixed_handicap(Integer.parseInt(parser.nextToken()));
	         System.out.println("= ");
    	  } catch(Exception e){
    		  System.out.println("? handicap not an integer");
    	  }
      }
      
      else if(cmnd.compareTo("place_free_handicap") == 0){
    	  try{
    		  place_free_handicap(Integer.parseInt(parser.nextToken()));
    		  System.out.println("= ");
		  } catch(Exception e){
			  System.out.println("? handicap not an integer");
		  }
      }
      
      else if(cmnd.compareTo("set_free_handicap") == 0){
    	  ArrayList<String> vertices = new ArrayList<String>();
    	  //Grab the list of all the vertices.
    	  while(parser.countTokens() != 0){
    		  vertices.add(parser.nextToken());    		  
    	  }
    	  set_free_handicap(vertices);
    	  //We're Good.
    	  System.out.println("=");
      }
      
      else if(cmnd.compareTo("play") == 0){
    	  play(parser.nextToken(), parser.nextToken());
    	  //We're Good.
    	  System.out.println("=");
      }
      
      else if(cmnd.compareTo("genmove") == 0){
         System.out.println("= " + genmove(parser.nextToken()));
      }
      
      else if(cmnd.compareTo("undo") == 0){
         undo();
         System.out.println("= ");
      }
      
      else if(cmnd.compareTo("final_score") == 0){
         System.out.println("= " + final_score());
      }
      
      else if(cmnd.compareTo("final_status_list") == 0){
    	  System.out.print("= ");
    	  for(String response: final_status_list(parser.nextToken())){
    		  System.out.print(" " + response);
    	  }
    	  System.out.println();
      }
      
      else if(cmnd.compareTo("showboard") == 0){
         System.out.println("=");
         System.out.print(showboard());
      }
      
      else if(cmnd.compareTo("random") == 0) {
         System.out.println(random(parser.nextToken()));
      }

      else if(cmnd.compareTo("depth") == 0) {
         System.out.println(setDepth(parser.nextToken(),Integer.parseInt(parser.nextToken())));
         
      }

      else{
         System.out.println("? unknown command");
      }
      
      System.out.println("");// Put empty new line to indicate end of reply
   }
}
